/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_debug.cpp - debug visualization functions
//


#include "r_local.h"


/*
 ==================
 R_DebugAxis
 ==================
*/
void R_DebugAxis (const glqVec3 &origin, const glqMat3 &axis, float size, bool depthTest, int allowInView){

	glqVec4	color;
	glqVec3	forward, right, up;
	glqVec3	point;
	glqVec3	p1, p2;
	float	angle;
	float	s1, c1;
	float	s2, c2;
	int		i;

	for (i = 0; i < 6; i++){
		color = color_table[(i>>1) + 1];

		if (i & 1)
			point = origin - axis[i>>1] * size;
		else
			point = origin + axis[i>>1] * size;

		RB_AddDebugLine(color, origin, point, depthTest, allowInView);

		forward = point - origin;
		forward.Normalize();
		forward.NormalVectors(right, up);

		for (angle = 0.0f; angle < 360.0f; angle += 120.0f){
			glqMath::SinCos(DEG2RAD(angle), s1, c1);
			glqMath::SinCos(DEG2RAD(angle + 120.0f), s2, c2);

			s1 = s1 * 0.5f;
			c1 = c1 * 0.5f;
			s2 = s2 * 0.5f;
			c2 = c2 * 0.5f;

			p1 = point - forward + (right * c1) + (up * s1);
			p2 = point - forward + (right * c2) + (up * s2);

			RB_AddDebugLine(color, p1, point, depthTest, allowInView);
			RB_AddDebugLine(color, p1, p2, depthTest, allowInView);
		}
	}
}

/*
 ==================
 R_DebugLine
 ==================
*/
void R_DebugLine (const glqVec4 &color, const glqVec3 &start, const glqVec3 &end, bool depthTest, int allowInView){

	RB_AddDebugLine(color, start, end, depthTest, allowInView);
}

/*
 ==================
 R_DebugArrow
 ==================
*/
void R_DebugArrow (const glqVec4 &color, const glqVec3 &start, const glqVec3 &end, float size, bool depthTest, int allowInView){

	glqVec3	forward, right, up;
	glqVec3	p1, p2;
	float	angle;
	float	s1, c1;
	float	s2, c2;

	RB_AddDebugLine(color, start, end, depthTest, allowInView);

	forward = end - start;
	forward.Normalize();
	forward.NormalVectors(right, up);

	for (angle = 0.0f; angle < 360.0f; angle += 120.0f){
		glqMath::SinCos(DEG2RAD(angle), s1, c1);
		glqMath::SinCos(DEG2RAD(angle + 120.0f), s2, c2);

		s1 = s1 * 0.5f * size;
		c1 = c1 * 0.5f * size;
		s2 = s2 * 0.5f * size;
		c2 = c2 * 0.5f * size;

		p1 = end - (forward * size) + (right * c1) + (up * s1);
		p2 = end - (forward * size) + (right * c2) + (up * s2);

		RB_AddDebugLine(color, p1, end, depthTest, allowInView);
		RB_AddDebugLine(color, p1, p2, depthTest, allowInView);
	}
}

/*
 ==================
 R_DebugBounds
 ==================
*/
void R_DebugBounds (const glqVec4 &color, const glqBounds &bounds, bool depthTest, int allowInView){

	glqVec3	corners[8];

	bounds.ToPoints(corners);

	RB_AddDebugLine(color, corners[0], corners[4], depthTest, allowInView);
	RB_AddDebugLine(color, corners[1], corners[5], depthTest, allowInView);
	RB_AddDebugLine(color, corners[2], corners[6], depthTest, allowInView);
	RB_AddDebugLine(color, corners[3], corners[7], depthTest, allowInView);
	RB_AddDebugLine(color, corners[0], corners[2], depthTest, allowInView);
	RB_AddDebugLine(color, corners[1], corners[0], depthTest, allowInView);
	RB_AddDebugLine(color, corners[2], corners[3], depthTest, allowInView);
	RB_AddDebugLine(color, corners[3], corners[1], depthTest, allowInView);
	RB_AddDebugLine(color, corners[4], corners[6], depthTest, allowInView);
	RB_AddDebugLine(color, corners[5], corners[4], depthTest, allowInView);
	RB_AddDebugLine(color, corners[6], corners[7], depthTest, allowInView);
	RB_AddDebugLine(color, corners[7], corners[5], depthTest, allowInView);
}

/*
 ==================
 R_DebugSphere
 ==================
*/
void R_DebugSphere (const glqVec4 &color, const glqSphere &sphere, bool depthTest, int allowInView){

	glqVec3	point, lastPoint, lastPoints[18];
	float	angle1, s1, c1;
	float	angle2, s2, c2;
	int		i;

	point = sphere.Center();
	point.z += sphere.Radius();

	for (i = 0; i < 18; i++)
		lastPoints[i] = point;

	for (angle1 = 20.0f; angle1 <= 360.0f; angle1 += 20.0f){
		glqMath::SinCos(DEG2RAD(angle1), s1, c1);

		lastPoint.x = sphere.Center().x;
		lastPoint.y = sphere.Center().y + sphere.Radius() * s1;
		lastPoint.z = sphere.Center().z + sphere.Radius() * c1;

		i = 0;

		for (angle2 = 20.0f; angle2 <= 360.0f; angle2 += 20.0f){
			glqMath::SinCos(DEG2RAD(angle2), s2, c2);

			point.x = sphere.Center().x + sphere.Radius() * s2 * s1;
			point.y = sphere.Center().y + sphere.Radius() * c2 * s1;
			point.z = lastPoint.z;

			RB_AddDebugLine(color, lastPoint, point, depthTest, allowInView);
			RB_AddDebugLine(color, lastPoint, lastPoints[i], depthTest, allowInView);

			lastPoints[i] = lastPoint;
			lastPoint = point;

			i++;
		}
	}
}

/*
 ==================
 R_DebugBox
 ==================
*/
void R_DebugBox (const glqVec4 &color, const glqVec3 &origin, const glqMat3 &axis, const glqBounds &bounds, bool depthTest, int allowInView){

	glqVec3	corner, corners[8];
	int		i;

	for (i = 0; i < 8; i++){
		corner[0] = (i & 1) ? bounds[0][0] : bounds[1][0];
		corner[1] = (i & 2) ? bounds[0][1] : bounds[1][1];
		corner[2] = (i & 4) ? bounds[0][2] : bounds[1][2];

		corners[i] = origin + axis * corner;
	}

	RB_AddDebugLine(color, corners[0], corners[4], depthTest, allowInView);
	RB_AddDebugLine(color, corners[1], corners[5], depthTest, allowInView);
	RB_AddDebugLine(color, corners[2], corners[6], depthTest, allowInView);
	RB_AddDebugLine(color, corners[3], corners[7], depthTest, allowInView);
	RB_AddDebugLine(color, corners[0], corners[2], depthTest, allowInView);
	RB_AddDebugLine(color, corners[1], corners[0], depthTest, allowInView);
	RB_AddDebugLine(color, corners[2], corners[3], depthTest, allowInView);
	RB_AddDebugLine(color, corners[3], corners[1], depthTest, allowInView);
	RB_AddDebugLine(color, corners[4], corners[6], depthTest, allowInView);
	RB_AddDebugLine(color, corners[5], corners[4], depthTest, allowInView);
	RB_AddDebugLine(color, corners[6], corners[7], depthTest, allowInView);
	RB_AddDebugLine(color, corners[7], corners[5], depthTest, allowInView);
}

/*
 ==================
 R_DebugCone
 ==================
*/
void R_DebugCone (const glqVec4 &color, const glqVec3 &apex, const glqVec3 &direction, float apexRadius, float baseRadius, bool depthTest, int allowInView){

	glqVec3	forward, right, up;
	glqVec3	base;
	glqVec3	p1, lastP1;
	glqVec3	p2, lastP2;
	float	angle;
	float	s, c;

	forward = direction;
	forward.Normalize();
	forward.NormalVectors(right, up);

	base = apex + direction;

	lastP1 = apex - up * apexRadius;
	lastP2 = base - up * baseRadius;

	if (apexRadius == 0.0f){
		for (angle = 20.0f; angle <= 360.0f; angle += 20.0f){
			glqMath::SinCos(DEG2RAD(angle), s, c);

			p2 = base + ((right * s) - (up * c)) * baseRadius;

			RB_AddDebugLine(color, lastP2, p2, depthTest, allowInView);
			RB_AddDebugLine(color, p2, apex, depthTest, allowInView);

			lastP2 = p2;
		}
	}
	else {
		for (angle = 20.0f; angle <= 360.0f; angle += 20.0f){
			glqMath::SinCos(DEG2RAD(angle), s, c);

			p1 = apex + ((right * s) - (up * c)) * apexRadius;
			p2 = base + ((right * s) - (up * c)) * baseRadius;

			RB_AddDebugLine(color, p1, p2, depthTest, allowInView);
			RB_AddDebugLine(color, lastP1, p1, depthTest, allowInView);
			RB_AddDebugLine(color, lastP2, p2, depthTest, allowInView);

			lastP1 = p1;
			lastP2 = p2;
		}
	}
}

/*
 ==================
 R_DebugCircle
 ==================
*/
void R_DebugCircle (const glqVec4 &color, const glqVec3 &origin, const glqVec3 &direction, float radius, int steps, bool depthTest, int allowInView){

	glqVec3	forward, right, up;
	glqVec3	point, lastPoint;
	float	s, c;
	int		i;

	forward = direction;
	forward.Normalize();
	forward.NormalVectors(right, up);

	right *= radius;
	up *= radius;

	lastPoint = origin + up;

	for (i = 1; i <= steps; i++){
		glqMath::SinCos(M_PI_TWO * i / steps, s, c);

		point = origin + (right * s) + (up * c);

		RB_AddDebugLine(color, lastPoint, point, depthTest, allowInView);

		lastPoint = point;
	}
}

/*
 ==================
 R_DebugFrustum
 ==================
*/
void R_DebugFrustum (const glqVec4 &color, const glqVec3 &origin, const glqMat3 &axis, float xMin, float xMax, float yMin, float yMax, float zNear, float zFar, bool depthTest, int allowInView){

	glqVec3	corners[8];
	glqVec3	nVector, fVector;
	glqVec3	lVector, rVector;
	glqVec3	dVector, uVector;
	float	ratio;

	ratio = zFar / zNear;

	nVector = axis[0] * zNear;
	fVector = axis[0] * zFar;

	lVector = axis[1] * xMin;
	rVector = axis[1] * xMax;

	dVector = axis[2] * yMin;
	uVector = axis[2] * yMax;

	corners[0] = origin + fVector - lVector * ratio + uVector * ratio;
	corners[1] = origin + fVector - rVector * ratio + uVector * ratio;
	corners[2] = origin + fVector - lVector * ratio + dVector * ratio;
	corners[3] = origin + fVector - rVector * ratio + dVector * ratio;
	corners[4] = origin + nVector - lVector + uVector;
	corners[5] = origin + nVector - rVector + uVector;
	corners[6] = origin + nVector - lVector + dVector;
	corners[7] = origin + nVector - rVector + dVector;

	RB_AddDebugLine(color, corners[0], corners[4], depthTest, allowInView);
	RB_AddDebugLine(color, corners[1], corners[5], depthTest, allowInView);
	RB_AddDebugLine(color, corners[2], corners[6], depthTest, allowInView);
	RB_AddDebugLine(color, corners[3], corners[7], depthTest, allowInView);
	RB_AddDebugLine(color, corners[0], corners[2], depthTest, allowInView);
	RB_AddDebugLine(color, corners[1], corners[0], depthTest, allowInView);
	RB_AddDebugLine(color, corners[2], corners[3], depthTest, allowInView);
	RB_AddDebugLine(color, corners[3], corners[1], depthTest, allowInView);
	RB_AddDebugLine(color, corners[4], corners[6], depthTest, allowInView);
	RB_AddDebugLine(color, corners[5], corners[4], depthTest, allowInView);
	RB_AddDebugLine(color, corners[6], corners[7], depthTest, allowInView);
	RB_AddDebugLine(color, corners[7], corners[5], depthTest, allowInView);
}

/*
 ==================
 R_DebugPolygon
 ==================
*/
void R_DebugPolygon (const glqVec4 &color, int numPoints, const glqVec3 *points, bool fill, bool depthTest, int allowInView){

	RB_AddDebugPolygon(color, numPoints, points, fill, depthTest, allowInView);
}

/*
 ==================
 R_DebugText
 ==================
*/
void R_DebugText (const glqVec4 &color, bool forceColor, const glqVec3 &origin, float cw, float ch, const char *text, bool depthTest, int allowInView){

	RB_AddDebugText(color, forceColor, origin, cw, ch, text, depthTest, allowInView);
}